package thread;

class MyBlockingQueue {
    // 使用一个 String 类型的数组来保存元素. 假设这里只存 String.
    private String[] items = new String[1000];
    // 指向队列的头部
    //volatile 保证每次取和修改时拿到的都是最新的这个数据
    volatile int head = 0;
    // 指向队列的尾部的下一个元素. 总的来说, 队列中有效元素的范围 [head, tail)
    // 当 head 和 tail 相等(重合), 相当于空的队列.
    volatile private int tail = 0;
    // 使用 size 来表示元素个数.
    volatile private int size = 0;

    private Object locker = new Object();

    // 入队列
    public void put(String elem) throws InterruptedException {
        // 此处的写法就相当于直接把 synchronized 写到方法上了.
        synchronized (locker) {
            while (size >= items.length) { //循环判断防止interrupt等打断后未作修改的情况
                // 队列满了.
                // return;
                locker.wait(); //用locker相比于this能更有效地阻止interrupt打断this的情况
            }
            items[tail] = elem;
            tail++;
            if (tail >= items.length) {
                tail = 0;
            }
            size++;
            // 用来唤醒队列为空的阻塞情况
            locker.notify();
        }
    }

    // 出队列
    public String take() throws InterruptedException {
        synchronized (locker) {
            while (size == 0) {
                // 队列为空, 暂时不能出队列.
                // return null;
                locker.wait();
            }
            String elem = items[head];
            head++;
            if (head >= items.length) {
                head = 0;
            }
            size--;
            // 使用这个 notify 来唤醒队列满的阻塞情况
            locker.notify();
            return elem;
        }
    }
}

public class Demo20 {
    public static void main(String[] args) throws InterruptedException {
//        MyBlockingQueue queue = new MyBlockingQueue();
//        queue.put("aaa");
//        queue.put("bbb");
//        queue.put("ccc");
//
//        String elem = queue.take();
//        System.out.println("elem=" + elem);
//        elem = queue.take();;
//        System.out.println("elem=" + elem);
//        elem = queue.take();;
//        System.out.println("elem=" + elem);
//        elem = queue.take();;
//        System.out.println("elem=" + elem);

        // 创建两个线程, 表示生产者和消费者
        MyBlockingQueue queue = new MyBlockingQueue();
        Thread t1 = new Thread(() -> {
            int count = 0;
            while (true) {
                try {
                    queue.put(count + "");
                    System.out.println("生产元素: " + count);
                    count++;
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        Thread t2 = new Thread(() -> {
            while (true) {
                try {
                    String count = queue.take();
                    System.out.println("消费元素: " + count);

                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });

        t1.start();
        t2.start();
    }
}
